/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_int_var.h                                                          *
 ****************************************************************************/

/**
 * @file elisa_int_var.h
 * @brief Classes for Int variables.
 */


#ifndef _elisa_int_var_h_
#define _elisa_int_var_h_ 1

#include <stdlib.h>
#include <string>
#include <iostream>
#include <set>

#include "elisa_variable.h"
#include "elisa_real_var.h"
#include "elisa_interval.h"
#include "elisa_defs.h"

NAMESPACE_ELISA
/**
 * @class IntVarImplementation
 * @brief implementation of integer variable
 */
class IntVarImplementation : public RealVarImpl
{
public:
	/**
 	 * Constructor
 	 * @param var the symbolic variable linked to this
 	 * @param st the searchtree where the implementation is built
 	 */
	IntVarImplementation(Variable *var,
	                     SearchTree *searchTree, const Interval & i) : RealVarImpl(var, searchTree, i)
	{
		RealVarImpl::setPrecision(0.0);
	}

	/// The current domain takes value I
	void setDomain(const Interval & I);

	/// The current domain takes value I intersection current domain
	void setInterDomain(const Interval & I);

	/// I is the new domain
	void setQuietDomain(const Interval & I);

	/// Splitting of current domain
	void split() const;

	/// The domain of THIS in b := a point in b
	void toPoint(Box *b) const;

	void updateSymbolicDomain() const;

	/**
 	 * give the type of the VariableImplementation
 	 * @return a string given the name of the type
 	 */
	virtual std::string getType() const
	{
		return "IntVarImplementation";
	}
};


/**
 * @class IntSet
 * Domain represented by a set of integer
 */
class IntSet : public Domain, public std::set<int, ltint>
{
public:
	/**
 	 * Construtor
 	 */
	IntSet()
		: Domain(), std::set<int, ltint>()
	{
	}

	IntSet(const IntSet & d) 
		: Domain(d), std::set<int, ltint>(d)
	{
		//_val = d._val;
	}

	/**
 	 * Add an integer in the set
 	 */
	/*
	void addValue(const int i)
	{
		_val.insert(i);
	}
	*/

	/**
 	 * Iterator throught the set
 	 */
	/*
	std::set<int,ltint>::iterator begin() const
	{
		return _val.begin();
	}
	*/

	/**
 	 * Iterator throught the set
 	 */
	/*
	std::set<int,ltint>::iterator end() const
	{
		return _val.end();
	}
	*/

	/**
 	 * Empty the set
 	 */
	/*
	void clear()
	{
		_val.clear();
	}
	*/

	/**
 	 * Print the set
 	 */
	void print(std::ostream & os) const
	{
		std::set<int, ltint>::const_iterator it;
		os << "{";
		it = begin();
		if (it != end()) {
			os << *it;
		}
		++it;
		for (; it != end(); ++it) {
			os << ", " << *it;
		}
		os << "}";
	}

	bool isDifferent(const Domain & d) const
	{
		IntSet *i = dynamic_cast<IntSet *>(const_cast<Domain *>(&d));

		bool contient;

		if (i != 0) {
			std::set<int, ltint>::const_iterator it, it_this;
			for (it = i->begin(); it != i->end(); it++) {
				contient = false;

				for (it_this = begin(); it_this != end(); ++it_this) {
					if ((*it) == (*it_this)) {
						contient = true;
						break;
					}
				}

				if (!contient)
					return true;
			}

			return false;
		} else {
			return true;
		}
	}

	double getWidth() const
	{
		return size();
	}

private:
	//std::set<int, ltint> _val;
};


/**
 * @ingroup RealConstraint
 * @class IntVar
 * @brief Constrained integer variable
 */
class IntVar : public Variable
{
public:

	/**
 	 * @brief constructor
 	 * build a new RealIntVar given an interval and a optionnaly a name
 	 */
	IntVar(const IntSet & dom, const std::string & name = "") 
		: Variable(new IntSet(), name)
	{
		// ishii std::set<int,ltint>::iterator it;
		/*
		std::set<int, ltint>::const_iterator it;
		for (it = this->domain().begin(); it != this->domain().end(); ++it) {
			this->domain().addValue(*it);
		}
		*/
		domain().insert(dom.begin(), dom.end());
	}

	/**
 	 * @brief constructor
 	 * build a new RealIntVar given the upper and the lower bound of the interval and a optionnaly a name
 	 */
	IntVar (const int l, const int u, const std::string & name = "") 
		: Variable(new IntSet(), name)
	{
		for (int i = l; i <= u; ++i) {
			this->domain().insert(i);
		}
	}

	/**
 	 * pointer to the domain of the variable
 	 * @Return the interval domain
 	 */
	IntSet & domain()
	{
		return *(static_cast<IntSet *>(this->getDomain()));
	}

	IntSet const_domain() const
	{
		return *(static_cast<IntSet *>(this->getDomain()));
	}

	/// Creation in m of a copy of THIS
	Variable *copy(SearchTree *searchTree) const;

	/**
 	 * @brief function to build the internal representation
 	 * it calls the factory class
 	 */
	virtual void buildImplementation(SearchTree *searchTree);

private:
	// copy protection
	IntVar (const IntVar & v) : Variable(v)
	{
	};

	// copy protection
	IntVar & operator=(const IntVar & v)
	{
	};
};
END_NAMESPACE_ELISA
#endif // ELISA_INT_VAR
